package com.platform.component.excel.exporter.impl;

import com.platform.batch.base.util.ColumnConfig;
import com.platform.batch.base.util.XportConfig;
import com.platform.component.excel.exporter.Exporter;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;


/**
 * Title: SimpleExcelExporter
 * Description: 简单的excel导出器
 * Date 2020/5/8
 *
 * @author wxw
 * @version v1.0
 */

@SuppressWarnings("all")
public class SimpleExcelExporter implements Exporter {

    static final Integer DEFAULT_TITLE_ROW_NUM = 1;
    static final Integer WORKBOOK_CACHE_ROWS = 2000;

    private SXSSFWorkbook wb = null;
    private SXSSFSheet sheet = null;
    private OutputStream os = null;
    protected XportConfig config;
    private File excelFile;
    private Integer currRowNum = 0;
    private XSSFCellStyle textStyle = null;

    public SimpleExcelExporter(XportConfig config, String outFilePath) {
        this.config = config;
        this.config.columns = Arrays.stream(this.config.columns).filter(col -> Objects.nonNull(col) && Objects.nonNull(col.fieldName))
                .toArray(ColumnConfig[]::new);
        this.excelFile = buildOutputFile(outFilePath);

    }

    @Override
    public synchronized Map<String, Object> put(Map<String, Object> record) {
        // 忽略空记录
        if (isEmptyRecord(record)) {
            return record;
        }

        Row row = sheet.getRow(this.currRowNum);
        if (row == null) {
            row = sheet.createRow(this.currRowNum);
        }

        Cell cell;
        int colNum;
        for (int i = 0; i < this.config.columns.length; i++) {
            String fieldName = config.columns[i].fieldName;
            if (fieldName == null) {
                continue;
            }
            Object fieldValue = record.get(fieldName);
            colNum = this.config.firstColNum + i;
            cell = row.getCell(colNum);
            if (cell == null) {
                cell = row.createCell(i);
            }
            cell.setCellStyle(textStyle);
            if (fieldValue == null) {
                cell.setCellType(CellType.BLANK);
            } else {
                cell.setCellValue(fieldValue.toString());
            }
        }
        currRowNum++;

        long max_row_num = 1000000L;
        if (max_row_num < currRowNum) {
            refresh();
            openSheet();
            putTitle();
            this.currRowNum = config.firstRowNum;
        }
        return record;
    }
    /**
     * 创建文本单元格样式
     *
     * @param wb 工作簿对象
     * @return 样式对象
     */
    private static XSSFCellStyle createTextCellStyle(SXSSFWorkbook wb) {
        DataFormat fmt = wb.createDataFormat();
        XSSFCellStyle textStyle = (XSSFCellStyle) wb.createCellStyle();
        textStyle.setDataFormat(fmt.getFormat("@"));
        Font font = wb.createFont();
        font.setFontName("微软雅黑");
        font.setFontHeightInPoints((short) 10);
        textStyle.setFont(font);
        return textStyle;
    }
    @Override
    public synchronized void putTitle() {
        // 创建头部单元格
        createHead();
        for (int i = 0; i < config.columns.length; i++) {
            ColumnConfig cc = config.columns[i];
            if (cc == null || (cc.title == null && cc.fieldName == null)) {
                continue;
            }
            int rowNum = config.titleRowNum == null ? DEFAULT_TITLE_ROW_NUM : config.titleRowNum;
            Row row = sheet.getRow(rowNum);
            if (row == null) {
                row = sheet.createRow(rowNum);
            }

            int colNum = config.firstColNum + i;
            Cell cell = row.getCell(colNum);
            if (cell == null) {
                cell = row.createCell(colNum);
            }

            String title = cc.title == null ? cc.fieldName : cc.title;
            if (title == null) {
                cell.setCellType(CellType.BLANK);
            } else {
                cell.setCellValue(title);
            }
            sheet.setColumnWidth(colNum, cc.getTitle().getBytes().length * 256 + 768);
            sheet.setDefaultColumnStyle(colNum, textStyle);
        }
    }

    private synchronized void createHead() {
        if (Objects.nonNull(config.context.get("headRow"))) {
            Row headRow = sheet.createRow(0);
            Cell headCell = headRow.createCell(0);
            headCell.setCellValue((String) config.context.get("headRow"));
            headCell.setCellStyle(createHeadCellStyle(wb));
            // 合并头部单元格
            // 增加此判断，否则会报错
            if (config.columns.length > 1){
                CellRangeAddress region = new CellRangeAddress(0, 0, (short) 0, (short) config.columns.length - 1);
                sheet.addMergedRegion(region);
            }
        }
    }

    @Override
    public synchronized void close() throws Exception {
        if (this.wb != null) {
            try {
                if (this.os != null) {
                    this.os.close();
                } else if (this.excelFile != null) {
                    try (FileOutputStream fos = new FileOutputStream(this.excelFile)) {
                        this.wb.write(fos);
                    }
                }
            } finally {
                this.wb.close();
                this.wb.dispose();
            }
        }
    }

    @Override
    public void refresh() {
        if (this.sheet != null) {
            try {
                this.sheet.flushRows(5000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void open() {
        this.currRowNum = config.firstRowNum;
        this.wb = new SXSSFWorkbook(WORKBOOK_CACHE_ROWS);
        this.wb.setCompressTempFiles(true);
        String sheetName = config.sheetName;
        if (sheetName != null && sheetName.length() > 0) {
            openSheet(sheetName);
        } else {
            openSheet();
        }
        this.textStyle = createTextCellStyle(this.wb);
    }

    /**
     * 构建输出文件
     */
    private File buildOutputFile(String outFilePath) {
        File excelFile = new File(outFilePath);
        createParentPath(excelFile);
        return excelFile;
    }

    private void openSheet(String sheetName) {
        if (this.wb != null) {
            this.sheet = this.wb.createSheet(sheetName);
        }
    }

    private void openSheet() {
        if (this.wb != null) {
            this.sheet = this.wb.createSheet();
        }
    }

    private boolean isEmptyRecord(Map<String, Object> record) {
        return record == null || record.isEmpty();
    }


    private void createParentPath(File file) {
        File parentFile = file.getParentFile();
        if (null != parentFile && !parentFile.exists()) {
            parentFile.mkdirs();
            createParentPath(parentFile);
        }
    }

    /**
     * 创建头部单元格样式
     *
     * @param workbook 工作簿对象
     * @return 样式对象
     */
    private static XSSFCellStyle createHeadCellStyle(SXSSFWorkbook workbook) {
        XSSFCellStyle cellStyle = (XSSFCellStyle) workbook.createCellStyle();
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        return cellStyle;
    }
}
